/******************************************************************************* 
* KindCommand - a Javascript Library for WYSIWYG editor 
* Copyright (C) 2010 Longhao Luo 
* 
* @site http://www.kindsoft.net/ 
* @licence LGPL(http://www.opensource.org/licenses/lgpl-license.php) 
* @version 1.0 (20100423) 
*******************************************************************************/ 

(function (window) {

var args = [], functions = {};

var KC = function(expr, root) {
	if (typeof expr == 'string') {
		args = [KC.query(expr, root)];
	} else {
		args = [expr];
	}
	return functions;
};

function extend(key, val) {
	KC[key] = val;
	if (typeof val == 'function') {
		functions[key] = function() {
			for (var i = 0, len = arguments.length; i < len; i++) {
				args.push(arguments[i]);
			}
			var rtn = val.apply(KC, args);
			args = [args.shift()];
			return rtn;
		};
	}
}

KC.extend = function(key, val) {
	if (typeof key == 'object') {
		for (var k in key) extend(k, key[k]);
	} else {
		extend(key, val);
	}
};

KC.extend('browser', (function() {
	var ua = navigator.userAgent.toLowerCase();
	return {
		VERSION: ua.match(/(?:msie|firefox|webkit|opera)[\/:\s](\d+)/) ? RegExp.$1 : '0',
		IE: (ua.indexOf('msie') > -1 && ua.indexOf('opera') == -1),
		GECKO: (ua.indexOf('gecko') > -1 && ua.indexOf('khtml') == -1),
		WEBKIT: (ua.indexOf('applewebkit') > -1),
		OPERA: (ua.indexOf('opera') > -1)
	};
})());

window['KC'] = KC;

})(window);

(function (window) {

KC.extend({
	trim : function(str) {
		return str.replace(/^\s+|\s+$/g, '');
	},
	escape : function(str) {
		str = str.replace(/&/g, '&amp;');
		str = str.replace(/</g, '&lt;');
		str = str.replace(/>/g, '&gt;');
		str = str.replace(/"/g, '&quot;');
		return str;
	},
	unescape : function(str) {
		str = str.replace(/&lt;/g, '<');
		str = str.replace(/&gt;/g, '>');
		str = str.replace(/&quot;/g, '"');
		str = str.replace(/&amp;/g, '&');
		return str;
	},
	toHex : function(str) {
		function hex(s) {
			s = parseInt(s).toString(16).toUpperCase();
			return s.length > 1 ? s : '0' + s;
		}
		return str.replace(/rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/ig,
			function($0, $1, $2, $3) {
				return '#' + hex($1) + hex($2) + hex($3);
			}
		);
	},
	formatUrl : function(url, mode, host, pathname) {
		if (!mode) return url;
		mode = mode.toLowerCase();
		if (!KC.inArray(mode, ['absolute', 'relative', 'domain'])) return url;
		host = host || location.protocol + '//' + location.host;
		pathname = pathname || (location.pathname.match(/^(\/.*)\//) ? RegExp.$1 : '');
		if (url.match(/^(\w+:\/\/[^\/]*)/)) {
			if (RegExp.$1 !== host) return url;
		} else if (url.match(/^\w+:/)) {
			return url;
		}
		var getRealPath = function(path) {
			var parts = path.split('/');
			paths = [];
			for (var i = 0, len = parts.length; i < len; i++) {
				var part = parts[i];
				if (part == '..') {
					if (paths.length > 0) paths.pop();
				} else if (part !== '' && part != '.') {
					paths.push(part);
				}
			}
			return '/' + paths.join('/');
		};
		if (url.match(/^\//)) {
			url = host + getRealPath(url.substr(1));
		} else if (!url.match(/^\w+:\/\//)) {
			url = host + getRealPath(pathname + '/' + url);
		}
		if (mode == 'relative') {
			var getRelativePath = function(path, depth) {
				if (url.substr(0, path.length) === path) {
					var arr = [];
					for (var i = 0; i < depth; i++) {
						arr.push('..');
					}
					var prefix = '.';
					if (arr.length > 0) prefix += '/' + arr.join('/');
					if (pathname == '/') prefix += '/';
					return prefix + url.substr(path.length);
				} else {
					if (path.match(/^(.*)\//)) {
						return getRelativePath(RegExp.$1, ++depth);
					}
				}
			};
			url = getRelativePath(host + pathname, 0).substr(2);
		} else if (mode == 'absolute') {
			if (url.substr(0, host.length) === host) {
				url = url.substr(host.length);
			}
		}
		return url;
	},
	formatStyle : function(style) {
		style = style.replace(/\s*([^\s]+?)\s*:(.*?)(;|$)/g, function($0, $1, $2) {
			var k = $1.toLowerCase();
			var v = KC.trim($2.toLowerCase());
			v = KC.toHex(v);
			return k + ':' + v + ';';
		});
		return KC.trim(style);
	},
	formatHtml : function(html, urlType) {
		function toHash(tags) {
			var arr = tags.split(/,/);
			var hash = {};
			for (var i = 0, len = arr.length; i < len; i++) {
				hash[arr[i]] = null;
			}
			return hash;
		}
		var singleTagHash = toHash(KC.SINGLE_TAGS);
		var inlineTagHash = toHash(KC.INLINE_TAGS);
		var re = /<(\/)?([\w-:]+)((?:\s+|(?:\s+[\w-:]+)|(?:\s+[\w-:]+=[^\s"'<>]+)|(?:\s+[\w-:]+="[^"]*")|(?:\s+[\w-:]+='[^']*'))*)(\/)?>/g;
		html = html.replace(re, function($0, $1, $2, $3, $4) {
			var startSlash = $1 || '';
			var tagName = $2.toLowerCase();
			var attr = $3 || '';
			var endSlash = $4 ? ' ' + $4 : '';
			if (endSlash === '' && tagName in singleTagHash) endSlash = ' /';
			var nl = '';
			if ((startSlash || endSlash) && !(tagName in inlineTagHash)) nl = '\n';
			if (attr !== '') {
				attr = attr.replace(/\s*([\w-:]+)=([^\s"'<>]+|"[^"]*"|'[^']*')/g, function($0, $1, $2) {
					var key = $1.toLowerCase();
					var val = $2 || '';
					if (val === '') {
						val = '""';
					} else {
						if (key === 'style') {
							val = val.substr(1, val.length - 2);
							val = KC.formatStyle(val);
							if (val === '') return '';
							val = '"' + val + '"';
						}
						if (KC.inArray(key, ['src', 'href'])) {
							if (val.charAt(0) === '"') {
								val = val.substr(1, val.length - 2);
							}
							val = KC.formatUrl(val, urlType);
						}
						if (val.charAt(0) !== '"') val = '"' + val + '"';
					}
					return ' ' + key + '=' + val + ' ';
				});
				attr = attr.replace(/\s+(checked|selected|disabled|readonly)(\s+|$)/ig, function($0, $1) {
					var key = $1.toLowerCase();
					return ' ' + key + '="' + key + '"' + ' ';
				});
				attr = KC.trim(attr);
				attr = attr.replace(/\s+/g, ' ');
				if (attr) attr = ' ' + attr;
				return '<' + startSlash + tagName + attr + endSlash + '>' + nl;
			} else {
				return '<' + startSlash + tagName + endSlash + '>' + nl;
			}
		});
		return html;
	}
});

})(window);
(function (window) {

KC.extend({
	each : function(obj, func) {
		if (KC.isArray(obj)) {
			for (var i = 0, len = obj.length; i < len; i++) {
				if (func(i, obj[i]) === false) break;
			}
		} else {
			for (var key in obj) {
				if (obj.hasOwnProperty(key)) {
					if (func(key, obj[key]) === false) break;
				}
			}
		}
	},
	isArray : function(obj) {
		return Object.prototype.toString.call(obj) === '[object Array]';
	},
	inArray : function(el, arr) {
		for (var i = 0, len = arr.length; i < len; i++) {
			if (el === arr[i]) return true;
		};
		return false;
	}
});

})();

(function (window) {

KC.extend({
	SINGLE_TAGS : 'br,hr,img,area,col,embed,input,param',
	INLINE_TAGS : 'b,del,em,font,i,span,strike,strong,sub,sup,u,img,embed'
});

KC.extend({
	index : function(node) {
		var i = 0;
		while (node) {
			node = node.previousSibling;
			i++;
		}
		return --i;
	},
	html : function(el) {
		var html = el.innerHTML.toLowerCase();
		html = html.replace(/\n/g, '');
		html = KC.formatHtml(html);
		return html;
	},
	isAncestor : function(ancestor, node) {
		while (node = node.parentNode) {
			if (node === ancestor) return true;
		}
		return false;
	},
	isSingle : function(el) {
		return (',' + KC.SINGLE_TAGS + ',').indexOf(',' + el.nodeName.toLowerCase() + ',') > -1;
	},
	isInline : function(el) {
		return (',' + KC.INLINE_TAGS + ',').indexOf(',' + el.nodeName.toLowerCase() + ',') > -1;
	},
	attr : function(el, key) {
		key = key.toLowerCase();
		var val = null;
		if (KC.browser.IE && KC.browser.VERSION < 8) {
			var div = el.ownerDocument.createElement('div');
			div.appendChild(el.cloneNode(false));
			var re = /\s+(?:([\w-:]+)|(?:([\w-:]+)=([^\s"'<>]+))|(?:([\w-:]+)="([^"]*)")|(?:([\w-:]+)='([^']*)'))(?=(?:\s|\/|>)+)/g;
			var arr, k, v, list = {};
			while ((arr = re.exec(div.innerHTML.toLowerCase()))) {
				k = arr[1] || arr[2] || arr[4] || arr[6];
				v = arr[1] || (arr[2] ? arr[3] : (arr[4] ? arr[5] : arr[7]));
				if (k === key) {
					val = v;
					break;
				}
			}
		} else {
			val = el.getAttribute(key, 2);
		}
		if (key === 'style' && val !== null) {
			val = KC.formatStyle(val);
		}
		return val;
	},
	eachNode : function(node, func, order) {
		order = (typeof order == 'undefined') ? true : order;
		function walk(parent) {
			if (!parent) return;
			var n = order ? parent.firstChild : parent.lastChild;
			if (!n) return;
			while (n) {
				var next = order ? n.nextSibling : n.previousSibling;
				if (func(n)) return true;
				if (walk(n)) return true;
				n = next;
			}
		};
		if (func(node)) return true;
		walk(node);
	},
	trimNode : function(node) {
		var doc = node.ownerDocument;
		function ltrim(str) {
			return str.replace(/^\s+/g, ''); 
		}
		function rtrim(str) {
			return str.replace(/\s+$/g, ''); 
		}
		var text, parent, prev, next, isRightBlank = false;
		KC.eachNode(node, function(n) {
			parent = n.parentNode;
			if (n.nodeType != 3) return;
			prev = n.previousSibling;
			next = n.nextSibling;
			text = n.nodeValue;
			if (prev && !KC.isInline(prev)) text = ltrim(text);
			if (next && !KC.isInline(next)) text = rtrim(text);
			if (!parent.previousSibling && !KC.isInline(parent.parentNode) && (!prev || !KC.isInline(prev))) text = ltrim(text);
			if (!parent.nextSibling && !KC.isInline(parent.parentNode) && (!next || !KC.isInline(next))) text = ltrim(text);
			if (isRightBlank) text = ltrim(text);
			text = text.replace(/\s+/g, ' ');
			isRightBlank = /\s+$/.test(text);
			if (KC.trim(text) === '') parent.removeChild(n);
			else parent.replaceChild(doc.createTextNode(text), n);
		});
	}
});

})(window);


(function (window) {

KC.extend({
	/**
		Only support the following selector syntax.
		Patterns:
		*: any element
		E: an element of type E
		E[foo]: an E element with a "foo" attribute
		E[foo="bar"]: an E element whose "foo" attribute value is exactly equal to "bar"
		E.warning: an E element whose class is "warning" (the document language specifies how class is determined)
		E#myid: an E element with ID equal to "myid"
		E F: an F element descendant of an E element
		E > F: an F element child of an E element
		Reference:
		Selectors Level 3: http://www.w3.org/TR/css3-selectors/
	*/
	query : function(expr, root) {
		var arr = KC.queryAll(expr, root);
		return arr.length > 0 ? arr[0] : null;
	},
	queryAll : function(expr, root) {
		root = root || document;
		function escape(str) {
			if (typeof str != 'string') return str;
			return str.replace(/([^\w\-])/g, '\\$1');
		}
		function stripslashes(str) {
			return str.replace(/\\/g, '');
		}
		function cmpTag(tagA, tagB) {
			return tagA === '*' || tagA.toLowerCase() === escape(tagB.toLowerCase());
		}
		function byId(id, tag, root) {
			var arr = [];
			var doc = root.ownerDocument || root;
			var el = doc.getElementById(stripslashes(id));
			if (el) {
				if (cmpTag(tag, el.nodeName) && KC.isAncestor(root, el)) arr.push(el);
			}
			return arr;
		}
		function byClass(className, tag, root) {
			var doc = root.ownerDocument || root;
			var arr = [];
			if (root.getElementsByClassName) {
				var els = root.getElementsByClassName(stripslashes(className));
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (cmpTag(tag, el.nodeName)) arr.push(el);
				}
			} else if (doc.querySelectorAll) {
				var els = doc.querySelectorAll((root.nodeName !== '#document' ? root.nodeName + ' ' : '') + tag + '.' + className);
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (KC.isAncestor(root, el)) arr.push(el);
				}
			} else {
				var els = root.getElementsByTagName(tag);
				className = ' ' + className + ' ';
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (el.nodeType == 1) {
						var cls = el.className;
						if (cls && (' ' + cls + ' ').indexOf(className) > -1) arr.push(el);
					}
				}
			}
			return arr;
		}
		function byName(name, tag, root) {
			var arr = [];
			var els = root.getElementsByName(stripslashes(name));
			for (var i = 0, len = els.length, el; i < len; i++) {
				el = els[i];
				if (cmpTag(tag, el.nodeName)) {
					if (el.getAttributeNode('name')) arr.push(el);
				}
			}
			return arr;
		}
		function byAttr(key, val, tag, root) {
			var arr = [];
			var els = root.getElementsByTagName(tag);
			for (var i = 0, len = els.length, el; i < len; i++) {
				el = els[i];
				if (el.nodeType == 1) {
					if (val === null) {
						if (KC.attr(el, key) !== null) arr.push(el);
					} else {
						if (val === escape(KC.attr(el, key))) arr.push(el);
					}
				}
			}
			return arr;
		}
		function select(expr, root) {
			var arr = [];
			var tag = expr.match(/^((?:\\.|[^.#\s\[<>])+)/) ? RegExp.$1.toLowerCase() : '*';
			if (expr.match(/#((?:[\w\-]|\\.)+)$/)) {
				arr = byId(RegExp.$1, tag, root);
			} else if (expr.match(/\.((?:[\w\-]|\\.)+)$/)) {
				arr = byClass(RegExp.$1, tag, root);
			} else if (expr.match(/\[((?:[\w\-]|\\.)+)\]/)) {
				arr = byAttr(RegExp.$1.toLowerCase(), null, tag, root);
			} else if (expr.match(/\[((?:[\w\-]|\\.)+)\s*=\s*['"]?((?:\\.|[^'"]+)+)['"]?\]/)) {
				var key = RegExp.$1.toLowerCase(), val = RegExp.$2;
				if (key === 'id') arr = byId(val, tag, root);
				else if (key === 'class') arr = byClass(val, tag, root);
				else if (key === 'name') arr = byName(val, tag, root);
				else arr = byAttr(key, val, tag, root);
			} else {
				var els = root.getElementsByTagName(tag);
				for (var i = 0, len = els.length, el; i < len; i++) {
					el = els[i];
					if (el.nodeType == 1) arr.push(el); 
				}
			}
			return arr;
		}
		var parts = [];
		var arr, re = /((?:\\.|[^\s>])+|[\s>])/g;
		while (arr = re.exec(expr)) {
			if (arr[1] !== ' ') parts.push(arr[1]);
		}
		var results = [];
		if (parts.length == 1) {
			return select(parts[0], root);
		}
		var el, isChild = false;
		for (var i = 0, len = parts.length; i < len; i++) {
			var part = parts[i];
			if (part === '>') {
				isChild = true;
				continue;
			}
			if (i > 0) {
				var els = [];
				KC.each(results, function(key, val) {
					KC.each(select(part, val), function(k, v) {
						if (isChild) {
							if (val === v.parentNode) els.push(v);
						} else {
							els.push(v);
						}
					});
				});
				results = els;
			} else {
				results = select(part, root);
			}
			if (results.length == 0) return []; 
		}
		return results;
	}
});

})(window);


(function (window) {

KC.extend({
	START_TO_START : 0,
	START_TO_END : 1,
	END_TO_END : 2,
	END_TO_START : 3
});

KC.extend('range', function(doc) {
	this.startContainer = doc;
	this.startOffset = 0;
	this.endContainer = doc;
	this.endOffset = 0;
	this.collapsed = true;
	this.commonAncestorContainer = doc;
	function updateCollapsed() {
		this.collapsed = (this.startContainer === this.endContainer && this.startOffset === this.endOffset);
	}
	function updateCommonAncestor() {
		function scan(node, func) {
			if (node === doc) return;
			while (node) {
				if (func(node)) return;
				node = node.parentNode;
			}
		}
		var nodes = [];
		scan(this.startContainer, function(node) {
			nodes.push(node);
		});
		var ancestor = doc;
		scan(this.endContainer, function(node) {
			if (KC.inArray(node, nodes)) {
				ancestor = node;
				return true;
			}
		});
		this.commonAncestorContainer = ancestor;
	}
	function compareAndUpdate() {
		var rangeA = new KC.range(doc);
		var rangeB = new KC.range(doc);
		rangeA.startContainer = rangeA.endContainer = this.startContainer;
		rangeA.startOffset = rangeA.endOffset = this.startOffset;
		rangeB.startContainer = rangeB.endContainer = this.endContainer;
		rangeB.startOffset = rangeB.endOffset = this.endOffset;
		if (rangeA.compareBoundaryPoints(KC.START_TO_START, rangeB) == 1) {
			this.startContainer = this.endContainer;
			this.startOffset = this.endOffset;
		}
	}
	this.setStart = function(node, offset) {
		this.startContainer = node;
		this.startOffset = offset;
		if (this.endContainer === doc) {
			this.endContainer = node;
			this.endOffset = offset;
		}
		compareAndUpdate.call(this);
		updateCollapsed.call(this);
		updateCommonAncestor.call(this);
	};
	this.setEnd = function(node, offset) {
		this.endContainer = node;
		this.endOffset = offset;
		if (this.startContainer === doc) {
			this.startContainer = node;
			this.startOffset = offset;
		}
		compareAndUpdate.call(this);
		updateCollapsed.call(this);
		updateCommonAncestor.call(this);
	};
	this.setStartBefore = function(node) {
		this.setStart(node.parentNode || doc, KC.index(node));
	};
	this.setStartAfter = function(node) {
		this.setStart(node.parentNode || doc, KC.index(node) + 1);
	};
	this.setEndBefore = function(node) {
		this.setEnd(node.parentNode || doc, KC.index(node));
	};
	this.setEndAfter = function(node) {
		this.setEnd(node.parentNode || doc, KC.index(node) + 1);
	};
	this.selectNode = function(node) {
		this.setStartBefore(node);
		this.setEndAfter(node);
	};
	this.selectNodeContents = function(node) {
		if (node.nodeType == 3 || KC.isSingle(node)) {
			this.selectNode(node);
		} else {
			if (!KC.browser.IE) KC.trimNode(node);
			if (node.firstChild) {
				this.setStartBefore(node.firstChild);
				this.setEndAfter(node.lastChild);
			} else {
				this.setStart(node, 0);
				this.setEnd(node, 0);
			}
		}
	};
	this.collapse = function(toStart) {
		if (toStart) this.setEnd(this.startContainer, this.startOffset);
		else this.setStart(this.endContainer, this.endOffset);
	};
	this.compareBoundaryPoints = function(how, range) {
		var rangeA = this.toNativeRange();
		var rangeB = range.toNativeRange();
		if (KC.browser.IE) {
			var arr = {};
			arr[KC.START_TO_START] = 'StartToStart';
			arr[KC.START_TO_END] = 'EndToStart';
			arr[KC.END_TO_END] = 'EndToEnd';
			arr[KC.END_TO_START] = 'StartToEnd';
			var cmp = rangeA.compareEndPoints(arr[how], rangeB);
			if (cmp !== 0) return cmp;
			var nodeA, nodeB;
			if (how === KC.START_TO_START || how === KC.END_TO_START) nodeA = this.startContainer;
			if (how === KC.START_TO_END || how === KC.END_TO_END) nodeA = this.endContainer;
			if (how === KC.START_TO_START || how === KC.START_TO_END) nodeB = range.startContainer;
			if (how === KC.END_TO_END || KC.END_TO_START) nodeB = range.endContainer;
			if (nodeA === nodeB) return 0;
			if (how === KC.START_TO_START || how === KC.END_TO_START) return KC.isAncestor(nodeA, nodeB) ? -1 : 1;
			if (how === KC.END_TO_END || how === KC.START_TO_END) return KC.isAncestor(nodeA, nodeB) ? 1 : -1;
		} else {
			return rangeA.compareBoundaryPoints(how, rangeB);
		}
	};
	this.cloneRange = function() {
		var range = new KC.range(doc);
		range.setStart(this.startContainer, this.startOffset);
		range.setEnd(this.endContainer, this.endOffset);
		return range;
	};
	this.toString = function() {
		var nativeRange = this.toNativeRange();
		return KC.nativeRangeText(nativeRange);
	};
	this.extractContents = function() {
		// TODO
	};
	this.cloneContents = function() {
		// TODO
	};
	this.toNativeRange = function() {
		var startContainer = this.startContainer;
		var startOffset = this.startOffset;
		var endContainer = this.endContainer;
		var endOffset = this.endOffset;
		return KC.createNativeRange(doc, startContainer, startOffset, endContainer, endOffset);
	};
});

function getBeforeLength(node) {
	var doc = node.ownerDocument;
	var len = 0;
	var sibling = node.previousSibling;
	while (sibling) {
		if (sibling.nodeType == 1) {
			if (KC.isSingle(sibling)) {
				len += 1;
			} else {
				var range = KC.createNativeRange(doc);
				range.moveToElementText(sibling);
				len += range.text.length;
			}
		} else if (sibling.nodeType == 3) {
			len += sibling.nodeValue.length;
		}
		sibling = sibling.previousSibling;
	}
	return len;
}

function getEndRange(node, offset) {
	var doc = node.ownerDocument || node;
	var range = KC.createNativeRange(doc);
	if (doc == node) {
		range.collapse(true);
		return range;
	}
	if (node.nodeType == 1) {
		var children = node.childNodes;
		var isStart, child, isTemp = false;
		if (offset == 0) {
			child = children[0];
			isStart = true;
		} else {
			child = children[offset - 1];
			isStart = false;
		}
		if (!child) {
			var temp = doc.createTextNode(' ');
			node.appendChild(temp);
			child = temp;
			isTemp = true;
		}
		if (child.nodeName.toLowerCase() === 'head') {
			if (offset === 1) isStart = true;
			if (offset === 2) isStart = false;
			range.collapse(isStart);
			return range;
		}
		if (child.nodeType == 1) {
			range.moveToElementText(child);
			range.collapse(isStart);
		} else {
			range.moveToElementText(node);
			if (isTemp) node.removeChild(temp);
			var len = getBeforeLength(child);
			len = isStart ? len : len + child.nodeValue.length;
			range.moveStart('character', len);
		}
	} else if (node.nodeType == 3) {
		range.moveToElementText(node.parentNode);
		range.moveStart('character', offset + getBeforeLength(node));
	}
	return range;
}

KC.extend({
	createRange : function(doc) {
		return new KC.range(doc);
	},
	toRange : function(nativeRange) {
		if (KC.browser.IE) {
			var doc = nativeRange.parentElement().ownerDocument;
			if (nativeRange.item) {
				var range = new KC.range(doc);
				range.selectNode(nativeRange.item(0));
				return range;
			}
			function getStartEnd(isStart) {
				var range = new KC.range(doc);
				var pointRange = nativeRange.duplicate();
				pointRange.collapse(isStart);
				var parent = pointRange.parentElement();
				var children = parent.childNodes;
				if (children.length == 0) {
					range.selectNode(parent);
					return {node: range.startContainer, offset: range.startOffset};
				}
				var startNode = doc, startPos = 0, isEnd = false;
				var testRange = nativeRange.duplicate();
				testRange.moveToElementText(parent);
				for (var i = 0, len = children.length; i < len; i++) {
					var node = children[i];
					var cmp = testRange.compareEndPoints('StartToStart', pointRange);
					if (cmp > 0) isEnd = true;
					if (cmp == 0) {
						var range = new KC.range(doc);
						if (node.nodeType == 1) range.selectNode(node);
						else range.setStartBefore(node);
						return {node: range.startContainer, offset: range.startOffset};
					}
					if (node.nodeType == 1) {
						var nodeRange = nativeRange.duplicate();
						nodeRange.moveToElementText(node);
						testRange.setEndPoint('StartToEnd', nodeRange);
						if (isEnd) startPos += nodeRange.text.length;
						else startPos = 0;
					} else if (node.nodeType == 3) {
						testRange.moveStart('character', node.nodeValue.length);
						startPos += node.nodeValue.length;
					}
					if (!isEnd) startNode = node;
				}
				if (!isEnd && startNode.nodeType == 1) {
					range.setStartAfter(parent.lastChild);
					return {node: range.startContainer, offset: range.startOffset};
				}
				testRange = nativeRange.duplicate();
				testRange.moveToElementText(parent);
				testRange.setEndPoint('StartToEnd', pointRange);
				startPos -= KC.nativeRangeText(testRange).length;
				return {node: startNode, offset: startPos};
			}
			var start = getStartEnd(true);
			var end = getStartEnd(false);
			var range = new KC.range(doc);
			range.setStart(start.node, start.offset);
			range.setEnd(end.node, end.offset);
			return range;
		} else {
			var doc = nativeRange.startContainer.ownerDocument;
			console.log(nativeRange);
			//KC.trimNode(nativeRange.startContainer);
			console.log(nativeRange);
			KC.trimNode(nativeRange.endContainer);
			console.log(nativeRange);
			var range = new KC.range(doc);
			range.setStart(nativeRange.startContainer, nativeRange.startOffset);
			range.setEnd(nativeRange.endContainer, nativeRange.endOffset);
			return range;
		}
	},
	createNativeRange : function(doc, startContainer, startOffset, endContainer, endOffset) {
		var range;
		if (doc.createRange) {
			range = doc.createRange();
			range.selectNodeContents(doc.body);
		} else {
			range = doc.body.createTextRange();
		}
		if (arguments.length == 1) {
			return range;
		} else {
			if (KC.browser.IE) {
				range.setEndPoint('StartToStart', getEndRange(startContainer, startOffset));
				range.setEndPoint('EndToStart', getEndRange(endContainer, endOffset));
			} else {
				range.setStart(startContainer, startOffset);
				range.setEnd(endContainer, endOffset);
			}
			return range;
		}
	},
	nativeRangeText : function(nativeRange) {
		var str = 'text' in nativeRange ? nativeRange.text : nativeRange.toString();
		return str.replace(/\r\n|\n|\r/g, '');
	}
});

})(window);

(function (window) {

KC.extend({
	execCommand : function(doc, cmdName, ui, value) {
		return this[cmdName](doc, value);
	},
	backColor : function(doc, value) {
		//console.log(value);
		return this;
	},
	bold : function(doc) {
		//console.log(doc);
		return this;
	}
});

})(window);
